ident = '$Id: build_ui.py 104 2006-05-25 22:06:43Z ahmad $'

'''App File Builder'''
import sys, pdb
import wx
import rex
import common
import images
import re
import base
from xml.sax 	import saxutils
from xml.dom	import minidom
from xml.dom.minidom import getDOMImplementation

CG_LIST = 100
RB_SUBS = 101
RB_LIST = 102
CB_ARCHIVE=103
ID_BUILD=104
ID_OBJECT_PASTE=105
ID_OBJECT_REMOVE=106
ID_VARSET_ADD=107
ID_VARSET_DEL=108



class binder_options_dialog( wx.Dialog ):
	def __init__( self, parent ):
		wx.Dialog.__init__( self, parent, -1, "Binder Options", wx.DefaultPosition, wx.Size( 350, 250 ), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.DIALOG_EX_METAL  )
		common.metalic( self )
		self.chooser = wx.CheckListBox(self, -1, choices=['Include all instances', 'Include all variables'])
		self.all_instances = False
		self.all_variables = False
		# Add the buttons
		self.button_sizer = wx.BoxSizer( wx.HORIZONTAL )
		self.build_btn = wx.Button( self, wx.ID_OK, "&Ok" )
		#wx.EVT_BUTTON( self, wx.ID_OK, self.OnOk )
		self.button_sizer.Add( (0,0), 1, 1 )
		self.button_sizer.Add( self.build_btn, 1, 0 )
		self.button_sizer.Add( (0,0), 1, 1 )
		btn = wx.Button( self, wx.ID_CANCEL, "&Close" )
		btn.SetDefault()
		self.button_sizer.Add( btn, 1, 0 )
		self.button_sizer.Add( (0,0), 1, 1 )
		# Add the Drag Box
		self.sizer = wx.BoxSizer( wx.VERTICAL )
		self.status_label = wx.StaticText( self, -1, "")
		self.sizer.Add( self.status_label, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 5 )
		self.sizer.Add( self.chooser, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 5 )
		self.sizer.Add( self.button_sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 5 )
		self.SetSizer( self.sizer )
		self.SetAutoLayout( 1 )
		# Windows needs this call
		self.Layout()

class suiBuilderDropTarget( wx.PyDropTarget ):
	def __init__( self, list_object, sui ):
		wx.PyDropTarget.__init__( self )
		self.list 	= list_object
		self.sui	= sui
		self.data   = wx.PyTextDataObject(" ")
		self.SetDataObject( self.data )

	def OnEnter( self, x, y, d ):
		return wx.DragCopy

	def OnLeave( self ):
		pass

	def OnDrop( self, x, y ):
		return self.list.IsEnabled()

	def OnDragOver( self, x, y, d ):
		if self.list.IsEnabled():
			return wx.DragCopy
		else:
			return wx.DragNone
			
	def OnData( self, x, y, d ):
		common.dprint ( "OnData: %d %d %d\n" % (x, y, d) )
		if self.GetData():
			data = self.data.GetText()			
			common.dprint("Dropped data", data )
			if self.sui.handle_dropped_pasted( data ):
				return wx.DragCopy
		return wx.DragNone

class build( wx.Dialog ):
	def __init__( self, parent, account ):
		title = "App Builder"
		wx.Dialog.__init__( self, parent, -1, title, wx.DefaultPosition, wx.Size( 450, 700 ), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.DIALOG_EX_METAL  )
		common.metalic( self )
		self.pid = 0			# the pid of the running job on the server
		self.objects = {}		# List of objects to publish to
		self.varsets = {} 		# Variables to s
		self.account = account
		self.current_oid = 0
		self.current_vid = 0
		mainSizer = wx.BoxSizer( wx.VERTICAL )
		# Create GridBag
		hdrSizer = wx.GridBagSizer( 2, 2 )
		# Add the Titles
		hdrSizer.Add( wx.StaticText( self, -1, "ESDnet Account Info:" ), (0,0), (1,2), wx.LEFT | wx.RIGHT, 20 )
		hdrSizer.Add( wx.StaticText( self, -1, "App Info:" ),(0,3), (1,2),  0, wx.LEFT | wx.RIGHT, 20 )
		#Add Esdnet Info
		self.esdnet_username = wx.TextCtrl( self, -1 )
		self.esdnet_password = wx.TextCtrl( self, -1, style = wx.TE_PASSWORD )
		hdrSizer.Add( wx.StaticText( self, -1, "Username:",style=wx.ALIGN_RIGHT ), (1,0), (1,1),  wx.EXPAND )
		hdrSizer.Add( self.esdnet_username,(1,1), (1,1),  wx.EXPAND )
		hdrSizer.Add( wx.StaticText( self, -1, "Password:",style=wx.ALIGN_RIGHT ), (2,0), (1,1),  wx.EXPAND )
		hdrSizer.Add( self.esdnet_password, (2,1), (1,1),  wx.EXPAND )
		#Add App Info
		hdrSizer.Add( wx.StaticText( self, -1, "App Name:",style=wx.ALIGN_RIGHT ), (1,3), (1,1), wx.EXPAND )
		self.appname = wx.TextCtrl( self, -1 )
		hdrSizer.Add( self.appname, (1,4), (1,1), wx.EXPAND )
		self.wholeaccount = wx.CheckBox( self, CB_ARCHIVE, "Build whole account" )
		hdrSizer.Add( self.wholeaccount, (2,3), (1,2), wx.LEFT | wx.RIGHT, 20 )
		
		# Pre/post script locations
		hdrSizer.Add( wx.StaticText( self, -1, "Pre-script:",style=wx.ALIGN_RIGHT ), (3,0), (1,1), wx.EXPAND )
		hdrSizer.Add( wx.StaticText( self, -1, "Post-script:",style=wx.ALIGN_RIGHT ), (4,0), (1,1), wx.EXPAND )
		self.preScript = wx.TextCtrl( self, -1 )
		self.postScript = wx.TextCtrl( self, -1 )
		hdrSizer.Add( self.preScript, (3,1), (1,3), wx.EXPAND )
		hdrSizer.Add( self.postScript, (4,1), (1,3), wx.EXPAND )
		
		mainSizer.Add( hdrSizer, 0, wx.EXPAND | wx.ALL, 10 )
		# The list
		mainSizer.Add(wx.StaticText(self,-1,"COMPONENTS:"))
		mainSizer.Add(wx.StaticText(self,-1,"Drag and drop items to the following box to include them in your app."))
				
		self.list = wx.ListCtrl( self, CG_LIST, style=wx.LC_REPORT )
		self.il = wx.ImageList(16, 16)
		# Load our images
		self.contact_icon 	= self.il.Add( images.getContactBitmap() )
		self.group_icon 	= self.il.Add( images.getGroupBitmap() )
		self.binder_icon 	= self.il.Add( images.getBinderBitmap() )
		self.variable_icon = self.il.Add( images.getVariableBitmap() )
		self.instance_icon = self.il.Add( images.getInstanceBitmap() )
		self.mirror_binder_icon = self.il.Add( images.getMirrorBinderBitmap() )
		self.system_binder_icon = self.il.Add( images.getSystemBinderBitmap() )
		self.list.SetImageList( self.il, wx.IMAGE_LIST_SMALL )
		mainSizer.Add( self.list, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 20 )
		info = wx.ListItem()
		info.m_mask = wx.LIST_MASK_TEXT # | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
		#info.m_image = -1
		info.m_format = 0
		info.m_text = "ID"
		self.list.InsertColumnInfo(0, info)
		info.m_text = "Name"
		self.list.InsertColumnInfo(1, info)
		info.m_text = "Kind"
		self.list.InsertColumnInfo(2, info)
		info.m_text = "Extra"
		self.list.InsertColumnInfo(3, info)
		# Set the drop target
		dt = suiBuilderDropTarget( self.list, self )
		self.list.SetDropTarget( dt )
		wx.EVT_RIGHT_DOWN( self.list, self.OnListRightClick )
		# Set the drag and drop events
		wx.EVT_LIST_BEGIN_DRAG( self.list, CG_LIST, self.OnBeginDrag )
		self.use_list = True
				
		mainSizer.AddSpacer(10)
		
		mainSizer.Add(wx.StaticText(self,-1,"APP COMMANDS:"))		
		mainSizer.Add(wx.StaticText(self,-1,"Configure variables to be set when this application is deployed"))
		mainSizer.Add(wx.StaticText(self,-1,"TABLE: Binder ID number, or \"contact\"."))
		mainSizer.Add(wx.StaticText(self,-1,"INSTANCE: An ID (for an instance or contact) or * to specify all items."))		
		# Add controls for adding/removing variable settings
		mainSizer.AddSpacer(2)
		varsetSizer = wx.GridBagSizer( 2, 4 )
		varsetSizer.Add(wx.StaticText(self,-1,"Table"),(0,0),(1,1),wx.LEFT | wx.RIGHT,20)
		varsetSizer.Add(wx.StaticText(self,-1,"Instance"),(0,1),(1,1),wx.LEFT | wx.RIGHT,20)
		varsetSizer.Add(wx.StaticText(self,-1,"Variable"),(0,2),(1,1),wx.LEFT | wx.RIGHT,20)
#		varsetSizer.Add(wx.StaticText(self,-1,"Value"),(0,3),(1,2),wx.LEFT | wx.RIGHT,20)
		
		self.vset_table = wx.TextCtrl(self,-1)
		varsetSizer.Add(self.vset_table,(1,0),(1,1))
		self.vset_instance = wx.TextCtrl(self,-1)
		varsetSizer.Add(self.vset_instance,(1,1),(1,1))
		self.vset_vname = wx.TextCtrl(self,-1)
		varsetSizer.Add(self.vset_vname,(1,2),(1,1))
		self.vset_value = wx.TextCtrl(self,-1,style=wx.TE_MULTILINE,size=(415,100))
		varsetSizer.Add(self.vset_value,(2,0),(1,4))
		mainSizer.Add( varsetSizer, 0, wx.EXPAND | wx.ALL, 10 )
		mainSizer.Add(wx.Button( self, ID_VARSET_ADD, "&Add variable assignment to application" ))
		wx.EVT_BUTTON( self, ID_VARSET_ADD, self.OnVarSetAdd )		
		
		
		# Add the list for variable setting, etc.
		mainSizer.AddSpacer(5)


		
		self.varsetlist = wx.ListCtrl( self, CG_LIST, style=wx.LC_REPORT )
		mainSizer.Add(self.varsetlist,1,wx.EXPAND | wx.LEFT | wx.RIGHT,20)
		headeritem = wx.ListItem()
		headeritem.m_mask = wx.LIST_MASK_TEXT
		headeritem.m_format = 0
		colindex = 0
		for label in ("Binder","Instance","Variable","Value"):
			headeritem.m_text = label
			self.varsetlist.InsertColumnInfo(colindex, headeritem)
			colindex = colindex + 1
		
		mainSizer.Add(wx.Button( self, ID_VARSET_DEL, "&Remove Selected" ))
		wx.EVT_BUTTON( self, ID_VARSET_DEL, self.OnVarSetDel )
		
		
		
		

		
		mainSizer.AddSpacer(10)
		# Add the major buttons
		self.button_sizer = wx.BoxSizer( wx.HORIZONTAL )
		self.build_btn = wx.Button( self, ID_BUILD, "&Build" )
		wx.EVT_BUTTON( self, ID_BUILD, self.OnBuild )
		self.button_sizer.Add( (0,0), 1, 1 )
		self.button_sizer.Add( self.build_btn, 1, 0 )
		self.button_sizer.Add( (0,0), 1, 1 )
		btn = wx.Button( self, wx.ID_CANCEL, "&Close" )
		btn.SetDefault()
		self.button_sizer.Add( btn, 1, 0 )
		self.button_sizer.Add( (0,0), 1, 1 )
		# Add the Drag Box
		self.mainSizer = wx.BoxSizer( wx.VERTICAL )
		self.mainSizer.Add( mainSizer, 1, wx.EXPAND | wx.NORTH | wx.SOUTH | wx.EAST | wx.WEST, 10 )
		self.status_label = wx.StaticText( self, -1, "")
		self.mainSizer.Add( self.status_label, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 20 )
		self.mainSizer.Add( self.button_sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 20 )
		self.SetSizer( self.mainSizer )
		self.SetAutoLayout( 1 )
		menu = wx.Menu()
		menu.Append( ID_OBJECT_PASTE, "&Paste", " Paste contacts or groups from the clipboard." )
		menu.Append( ID_OBJECT_REMOVE, "&Cut", " Remove the selected contacts or groups" )
		self.menu = menu
		wx.EVT_MENU( self, ID_OBJECT_PASTE, self.OnObjectPaste )
		wx.EVT_MENU( self, ID_OBJECT_REMOVE, self.OnObjectRemove )
		# Windows needs this call
		self.resize_columns()
		self.Layout()

	def OnDestroy(self,event):
		common.dprint("Destroying Build UI")
		self.account.window.build_dialog = None
		
	def OnListRightClick( self, event ):
		common.dprint("OnListRightClick")
		try:
			self.PopupMenu( self.menu, wx.Point( event.m_x + 5, event.m_y + 45) )
		except:
			pass

	def OnObjectPaste( self, event ):
		wx.TheClipboard.Open()
		clipdata = wx.TextDataObject()
		wx.TheClipboard.GetData( clipdata )
		wx.TheClipboard.Close()
		xmlstring = clipdata.GetText()
		self.handle_dropped_pasted( xmlstring )

	def OnObjectRemove( self, event ):
		sel = common.get_selected_in_list( self.list )
		if len( sel ) < 1: return		
		# Remove in reverse order, so that we're not scrambling the index positions
		# as we work.		
		sel.sort()
		sel.reverse()
		
		for item in sel:					
			oid  = self.list.GetItemData( item )			
			try:
				self.objects.__delitem__( oid )
				self.list.DeleteItem( item )
			except:
				pass

	def OnUseSubs( self, event ):
		self.list.Enable( False )
		self.use_list = False

	def OnUseList( self, event ):
		self.list.Enable( True )
		self.use_list = True

	def expandFolder(self, fname, recursive = True):
		
		# Fname is such a pattern that a prepended slash will result in
		# it's path as if the root were the webdav document root.
		
		# Spaghetti code warning! This one is the fault of Darien Hager
		# darien@etelos.com Mar 2007
		
		# Justification: Having the build UI use webdav information to expand
		# a drag-n-drop folder is FAR better than rewriting all the other drag
		# -and-drop functionality.
		
		# Originally thought of defining a new XML tag for the final output to
		# the server, but then people can't select all items but exclude one,
		# etc.
		
		retfiles = []
		davCon = self.GetParent().dav_control.davCon
		(subdirs, files) = davCon.get_dav_items('/'+str(fname))
		
		for davobj in files:
			# Strip leading slash, for compat with build stuff
			uri = davobj.uri.lstrip('/')
			retfiles.append(uri)										
		if(recursive and (len(subdirs) > 0)):			
			for davobj in subdirs:
				subpath = davobj.uri.lstrip('/')
				retfiles.extend(self.expandFolder(subpath))						
		return retfiles
		
		
	def OnBuild( self, event ):
		if self.pid > 0:
			self.CheckStatus()
			return
		common.dprint("Building ", self.objects )
		appname = self.appname.GetValue().strip()
		if appname == "":
			wx.MessageBox( "You must provide an App Name",
						"App Name error", wx.OK )
			return
		# change the app name
		appname = re.sub(r"[^\w_-]", "_", appname)
		esdnet_username = self.esdnet_username.GetValue().strip()
		esdnet_password = self.esdnet_password.GetValue().strip()
		if esdnet_username == "" or esdnet_password == "":
			wx.MessageBox( "You must provide an ESDNet Username and Password",
						"ESDNet account error", wx.OK )
			return
		# check the esdnet username and password
		edom = '<esdnet_authenticate esdnet_username="%s" esdnet_password="%s" />' % 	(esdnet_username, esdnet_password)
		euuid = self.account.execute( 'base', "esdnet_authenticate", dom=edom )
		common.dprint("RETURN ", euuid )
		if re.match("[\w]{32}", euuid) is None:
			wx.MessageBox( "ESDNet authentication failed",
						"ESDNet account error", wx.OK )
			return
		#Create XML
		impl = getDOMImplementation()
		#Create XML ROOT
		xmldoc = impl.createDocument(None, "ROOT", None) # TODO use lowercase
#		xmldoc = impl.createDocument(None, "root", None)
		root = xmldoc.documentElement
		root.setAttribute("name",appname)
		#Create XML BUILD
		build = xmldoc.createElement('BUILD')	#TODO lowercase tagnames
#		build = xmldoc.createElement('build')
		build.setAttribute("phase","creating")
		build.setAttribute("esdnet_username",esdnet_username)
		build.setAttribute("esdnet_password",esdnet_password)
		build.setAttribute("server_name",self.account.server)
		root.appendChild(build)
		if self.wholeaccount.IsChecked():
			build.setAttribute("whole_app","yes")
			
			spaths = {"pre":self.preScript.GetValue().strip(),
			"post":self.postScript.GetValue().strip()}
			
			for condition in spaths:
				value = spaths[condition]
				if(len(value) < 1):
					continue
					
				if(value[0] == "/"):
					wx.MessageBox( ("Your \"%s-\" script must be a relative path, e.g.:\n"%condition) +
					"directory/subdirectory/script_to.run",
						"Script path error", wx.OK )
					return
					
				# For whole builds, it's a little less practical to check for script existence... and since time is of the essence right now..
					
				script = xmldoc.createElement('script') # Currently, the ONLY lowercase tag in the entire XML doc. Blame Brian.
				script.setAttribute("type",condition)
				script.setAttribute("file",value)
				build.appendChild(script)
			
			
		else:
			build.setAttribute("whole_app","no")
			for chosen in self.objects:
				common.dprint("Object ",self.objects[chosen])
				rtype = self.objects[chosen][0]
				id = self.objects[chosen][1]
				name = self.objects[chosen][2]
				extra = self.objects[chosen][3]
				common.dprint("rtype ", rtype )
				common.dprint("id ", id )
				common.dprint("name ", name )
				common.dprint("extra ", extra )
				if rtype == "contact":
					build = xmldoc.createElement('CONTACT')	#TODO lowercase tagnames
#					build = xmldoc.createElement('contact')
					build.setAttribute("id",id)
					build.setAttribute("uuid",name)
					root.appendChild(build)
				elif rtype == "group":
					build = xmldoc.createElement('GROUP')	#TODO lowercase tagnames
#					build = xmldoc.createElement('group')
					build.setAttribute("id",id)
					build.setAttribute("name",name)
					root.appendChild(build)
				elif rtype == "publication":
					build = xmldoc.createElement('PUBLICATION')	#TODO lowercase tagnames
#					build = xmldoc.createElement('publication')
					build.setAttribute("id",id)
					build.setAttribute("name",name)
					root.appendChild(build)
				elif rtype == "issue":
					build = xmldoc.createElement('ISSUE')	#TODO lowercase tagnames
#					build = xmldoc.createElement('issue')
					build.setAttribute("id",id)
					build.setAttribute("pid","")
					root.appendChild(build)
				elif rtype == "binder":
					build = xmldoc.createElement('BINDER')	#TODO lowercase tagnames
#					build = xmldoc.createElement('binder')
					build.setAttribute("id",id)
					build.setAttribute("name",name)
					all_instances,all_variables = extra
					if all_instances:
						build.setAttribute("with_all_instances","1")
					else:
						build.setAttribute("with_all_instances","0")
					if all_variables:
						build.setAttribute("with_all_variables","1")
					else:
						build.setAttribute("with_all_variables","0")
					root.appendChild(build)
				elif rtype == "variable":
					build = xmldoc.createElement('VARIABLE')	#TODO lowercase tagnames
#					build = xmldoc.createElement('variable')
					build.setAttribute("binder",id)
					build.setAttribute("name",name)
					root.appendChild(build)
				elif rtype == "instance":
					build = xmldoc.createElement('INSTANCE')	#TODO lowercase tagnames
#					build = xmldoc.createElement('instance')
					build.setAttribute("id",id)
					build.setAttribute("bid",name)
					root.appendChild(build)
				elif rtype == "gcb":
					build = xmldoc.createElement('GCB')	#TODO lowercase tagnames
#					build = xmldoc.createElement('gcb')
					build.setAttribute("id",id)
					build.setAttribute("name",name)
					root.appendChild(build)
				elif rtype == "file":
					build = xmldoc.createElement('FILE')	#TODO lowercase tagnames
#					build = xmldoc.createElement('file')
					build.setAttribute("location",name)
					root.appendChild(build)		
			# Do varset items	
			
			itemid = self.varsetlist.GetNextItem(-1)
			while itemid >= 0:				
				values = self.varsets[self.varsetlist.GetItemData(itemid)]				
				assert(type(values) in (list,tuple))
				assert(len(values) == 4)
				values = [str(x) for x in values]
				build = xmldoc.createElement('VARSET')	#TODO lowercase tagnames
#				build = xmldoc.createElement('varset')
				build.setAttribute("binder",values[0])
				build.setAttribute("instance",values[1])
				build.setAttribute("variable",values[2])
				# Cannot use a child node, causes problems for server code
				# build.appendChild(xmldoc.createTextNode(values[3]))
				build.setAttribute("data",values[3])
				root.appendChild(build)
				itemid = self.varsetlist.GetNextItem(itemid)
				
			
				
			# Do pre/post scripting XML			
			
			# Script paths
			# 
			spaths = {"pre":self.preScript.GetValue().strip(),
			"post":self.postScript.GetValue().strip()}
			
			for condition in spaths:
				value = spaths[condition]
				if(len(value) < 1):
					continue
					
				if(value[0] == "/"):
					wx.MessageBox( ("Your \"%s-\" script must be a relative path, e.g.:\n"%condition) +
					"directory/subdirectory/script_to.run",
						"Script path error", wx.OK )
					return
					
				# Check that it exists in current build
				foundInBuild = False
				for chosen in self.objects:
					rtype = self.objects[chosen][0]
					if(rtype != "file"): 
						continue
					path = self.objects[chosen][2]
					if(path == value):
						foundInBuild = True
						break
					
				if(not foundInBuild):
					wx.MessageBox( ("Your \"%s-\"script does not exist in your list of files to build.\n"% condition)+
					"Note that paths must be in unix \"folder/subdir/file.ext\" format."  ,
						"Script path error", wx.OK )
					return
					
				build = xmldoc.createElement('script') # Currently, the ONLY lowercase tag in the entire XML doc. Blame Brian.
				build.setAttribute("type",condition)
				build.setAttribute("file",value)
				root.appendChild(build)
			
		cdom = xmldoc.toxml()
		common.dprint("XML ", cdom )
		pid = self.account.execute( 'base', "buildapp", DOM=cdom )
		pid = int(pid)
		common.dprint("returned pid ", pid )
		if pid < 1:
			wx.MessageBox( "Build Job failed",
						"Build Job failed, please contact the administrator.", wx.OK )
			return
		self.pid = pid
		# relabel the build button
		self.build_btn.SetLabel("Status")
		self.status_label.SetLabel("Build started ...")
		self.Layout()

	def CheckStatus(self):
		common.dprint("checking pid ", self.pid )
		still_running = self.account.execute( 'base', "still_running", pid=self.pid )
		still_running = still_running.strip()
		common.dprint("still running ", still_running )
		if still_running == '1':
			self.status_label.SetLabel("Processing ...")
		else:
			self.status_label.SetLabel("Done.")
			self.build_btn.SetLabel("Build")
			self.pid = 0
		
	def OnBeginDrag( self, event ):
		list = self.list
		sel = common.get_selected_in_list( list )
		if len( sel ) < 1: return
		oid  = list.GetItemData( sel[0] )
		data = wx.PyTextDataObject(" ")
		ds = wx.DropSource( self )
		ds.SetData( data )
		result = ds.DoDragDrop()
		if result == 5:
			# Remove the object
			try:
				self.objects.__delitem__( oid )
				list.DeleteItem( sel[0] )
			except:
				pass

	def AppendObject( self, type, id, name ):
		common.dprint( "AppendObject type=%s id=%s name=%s" % ( type, id, name ) )
		extra = ""
		xmlextra = "0"
		x = self.list.GetItemCount()
		if type == "contact":
			icon = self.contact_icon
		elif type == "group":
			icon = self.group_icon
		elif type == "binder":
			icon = self.binder_icon
			if type == "binder":
				dlg = binder_options_dialog( self )				
				res = dlg.ShowModal()
				all_instances = dlg.chooser.IsChecked(0)
				all_variables = dlg.chooser.IsChecked(1)
				dlg.Destroy()
				del( dlg )
				if res != wx.ID_OK:
					return
				extra = ''
				if all_instances:
					extra = " All Instances "
				if all_variables:
					extra = extra + " All Variables "
				xmlextra = ( all_instances, all_variables )
		elif type == "instance":
			icon = self.instance_icon
		else:
			icon = self.group_icon
		common.dprint("Adding ", type, "id ", id, " name", name )
		x = self.list.InsertImageStringItem( sys.maxint, id, icon )
		self.list.SetStringItem( x , 1, name )
		self.list.SetStringItem( x , 2, type )
		self.list.SetStringItem( x , 3, extra )
		oid = self.get_new_oid()
		self.objects[oid] = [type,id,name,xmlextra]
		self.list.SetItemData( x, oid )

	def handle_dropped_pasted( self, data ):
		common.dprint("Handle data", data )		
		xmlstring = data.strip()		
		common.dprint("XML STRING is : [%s]" % xmlstring )
		#parse the data
		sdom  = None
		snode = None
		datatype = "XML"
		try:
			sdom 	= minidom.parseString( xmlstring )
			snode	= sdom.firstChild
		except:
			datatype = "STRING"
		
		common.dprint(datatype);
		
		if datatype == "XML":
			common.dprint("Looking up ", snode.nodeName )
			common.dprint("XML ", snode.toxml() )
			for inode in snode.getElementsByTagName("contact"):
				type = "contact"
				id = inode.getAttribute("id")
				name = inode.getAttribute("uuid")
				self.AppendObject( type, id, name )
			for inode in snode.getElementsByTagName("group"):
				type = "group"
				id = inode.getAttribute("id")
				name = inode.getAttribute("name")
				self.AppendObject( type, id, name )
			if snode.nodeName == "publication":
				type = "publication"
				id = snode.getAttribute("id")
				name = snode.getAttribute("name")
				self.AppendObject( type, id, name )
			if snode.nodeName == "gcb":
				type = "gcb"
				id = snode.getAttribute("id")
				name = snode.getAttribute("rule") + " = " + snode.getAttribute("instances")
				self.AppendObject( type, id, name )
		elif datatype == "STRING":
			ins = re.match(r"(?P<bid>[0-9]*)\.(?P<id>[0-9]*)\|(?P<bname>[-a-zA-Z0-9_ ]*)\.(?P<iname>[-a-zA-Z0-9_ ]*)", data)
			bin = re.match(r"(?P<name>[-a-zA-Z0-9_ ]*)\.(?P<id>[0-9]*)", data)
			iss = re.match(r"<#\[(PAGE|EMAIL|ISSUE)_URL_(?P<id>[0-9]*)\]#>", data)
			var = re.match(r"<#\[(?P<binder>[-a-zA-Z0-9_ ]*)\.(?P<name>[-a-zA-Z0-9_ ]*)\]#>", data)
			afile = re.match(r"^\/(?P<name>[^;]*)", data)
			
			if ins:				
				self.AppendObject('instance',ins.group('id'),ins.group('bid'))
			elif bin:
				self.AppendObject('binder', bin.group("id"),bin.group("name"))
			elif iss:				
				self.AppendObject('issue', iss.group("id"), '' )
			elif var:				
				self.AppendObject('variable', var.group("binder"), var.group("name") )
			elif afile:
				# Includes both files are directories. Directories end in slashes
				
				if(afile.group("name").endswith("/")):
					# Folder, add multiple files
					pathstub = afile.group("name")
					
					choices = {'Only first level':False,'All contained files':True}										
					dlg = wx.SingleChoiceDialog(self, pathstub+'\nWhat items under this folder do you wish to add to the list?', 'Folder Selected',
					choices.keys(), 
					wx.CHOICEDLG_STYLE
					)
					
					if dlg.ShowModal() == wx.ID_OK:
						#true or false for recursion
						choice = choices[dlg.GetStringSelection()]
					else:
						choice = None
					dlg.Destroy()
					if(choice != None):						
						files = self.expandFolder(pathstub, choice)					
						for f in files:						
							self.AppendObject('file','', f )
				else:
					# Single file
					self.AppendObject( 'file', '', afile.group("name") )
				
		self.resize_columns()
		return True

	def get_new_oid( self ):
		self.current_oid = self.current_oid + 1
		return self.current_oid
	def get_new_vid( self ):
		self.current_vid = self.current_vid + 1
		return self.current_vid
	def resize_columns( self ):
		tables = (self.list , self.varsetlist)
		for j in tables:
				for i in range( j.GetColumnCount() ):
					j.SetColumnWidth( i, wx.LIST_AUTOSIZE )
	def OnVarSetDel(self, event):
		itemid = self.varsetlist.GetFirstSelected()
		if(itemid == -1): return(False)		
		vid = self.varsetlist.GetItemData(itemid)
		success = self.varsetlist.DeleteItem(itemid)
		if(success):			
			try:				
				del self.varsets[vid]
			except KeyError:
				return False
		return(success)
	def popupError(self, title, message):
		dlg = wx.MessageDialog(self, message,title, wx.OK | wx.ICON_ERROR )                               
		dlg.ShowModal()
		dlg.Destroy()
		
	def OnVarSetAdd(self, event):
		# Key is textctrl to check, value is list of valid non-numeric values
		# If list is empty, numeric is required. If None, then all values allowed.
		controls = [(self.vset_table,("Binder",["contact","contacts"])),
		(self.vset_instance,("Instance",["*"])),
		(self.vset_vname,("Variable name",None)),
		(self.vset_value,("Variable value",None))]
		errtitle = "Cannot add variable setting"
		data = []
		for i in controls:
			(ctrl,(ctrlname,nonintegervalues)) = i			
			val = ctrl.GetValue()
			if(len(val.strip()) < 1):
				self.popupError(errtitle,"The %s field is blank" % ctrlname)
				return(False)
			if(nonintegervalues == None): 
				data.append(val)
				continue
			
			try:
				val = int(val)
				data.append(val)
				continue
				
			except ValueError:
				if(val.lower() in nonintegervalues):
					if(val.lower() == "contacts"): val = "contact"
					data.append(val)
					continue
				else:
					if(nonintegervalues == None):
						self.popupError(errtitle,"The %s field must be an integer.")
					else:
						othervals = ' , '.join([str(x) for x in nonintegervalues])
						self.popupError(errtitle,"The "+ctrlname+" field must be either an integer or else one of the following:\n"+othervals)
					return(False)
		itemid = self.varsetlist.Append(data)
		
		if(itemid >= 0):
			vid = self.get_new_vid()
			self.varsets[vid] = data
			self.varsetlist.SetItemData(itemid,vid)
			
			self.varsetlist.EnsureVisible(itemid)
			self.resize_columns()
			for i in controls:
				(ctrl,(ctrlname,nonintegervalues)) = i			
				ctrl.SetValue('')
			return True
		else:
			return False
		

